﻿''' <summary>
''' Base class for screens that contain a menu of options. The user can
''' move up and down to select an entry, or cancel to back out of the screen.
''' </summary>
MustInherit Class MenuScreen
	Inherits GameScreen
#Region "Fields"

	' the number of pixels to pad above and below menu entries for touch input
	Const menuEntryPadding% = 10

	Dim _menuEntries As List(Of MenuEntry) = New List(Of MenuEntry)()
	Dim _selectedEntry% = 0
	Dim _menuTitle$

#End Region

#Region "Properties"


	''' <summary>
	''' Gets the list of menu entries, so derived classes can add
	''' or change the menu contents.
	''' </summary>
	Protected ReadOnly Property MenuEntries As IList(Of MenuEntry)
		Get
			Return _menuEntries
		End Get
	End Property


#End Region

#Region "Initialization"


	''' <summary>
	''' Constructor.
	''' </summary>
	Public Sub New(menuTitle$)
		' menus generally only need Tap for menu selection
		EnabledGestures = GestureType.Tap

		Me._menuTitle = menuTitle

		TransitionOnTime = TimeSpan.FromSeconds(0.5)
		TransitionOffTime = TimeSpan.FromSeconds(0.5)
	End Sub


#End Region

#Region "Handle Input"

	''' <summary>
	''' Allows the screen to create the hit bounds for a particular menu entry.
	''' </summary>
	Protected Overridable Function GetMenuEntryHitBounds(entry As MenuEntry) As Rectangle
		' the hit bounds are the entire width of the screen, and the height of the entry
		' with some additional padding above and below.
		Return New Rectangle( _
			0, _
			CInt(entry.Position.Y) - menuEntryPadding, _
			ScreenManager.GraphicsDevice.Viewport.Width, _
			entry.GetHeight(Me) + (menuEntryPadding * 2))
	End Function

	''' <summary>
	''' Responds to user input, changing the selected entry and accepting
	''' or cancelling the menu.
	''' </summary>
	Public Overrides Sub HandleInput(input As InputState)
		' we cancel the current menu screen if the user presses the back button
		Dim player As PlayerIndex
		If (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, player)) Then
			OnCancel(player)
		End If

		' look for any taps that occurred and select any entries that were tapped
		For Each gesture As GestureSample In input.Gestures
			If (gesture.GestureType = GestureType.Tap) Then
				' convert the position to a Point that we can test against a Rectangle
				Dim tapLocation As Point = New Point(CInt(gesture.Position.X), CInt(gesture.Position.Y))

				' iterate the entries to see if any were tapped
				For i% = 0 To menuEntries.Count - 1
					Dim menuEntry As MenuEntry = menuEntries(i)

					If (GetMenuEntryHitBounds(menuEntry).Contains(tapLocation)) Then
						' select the entry. since gestures are only available on Windows Phone,
						' we can safely pass PlayerIndex.One to all entries since there is only
						' one player on Windows Phone.
						OnSelectEntry(i, PlayerIndex.One)
					End If
				Next
			End If
		Next
	End Sub


	''' <summary>
	''' Handler for when the user has chosen a menu entry.
	''' </summary>
	Protected Overridable Sub OnSelectEntry(entryIndex%, PlayerIndex As PlayerIndex)
		menuEntries(entryIndex).OnSelectEntry(PlayerIndex)
	End Sub


	''' <summary>
	''' Handler for when the user has cancelled the menu.
	''' </summary>
	Protected Overridable Sub OnCancel(PlayerIndex As PlayerIndex)
		ExitScreen()
	End Sub


	''' <summary>
	''' Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
	''' </summary>
	Protected Sub OnCancel(sender As Object, e As PlayerIndexEventArgs)
		OnCancel(e.PlayerIndex)
	End Sub


#End Region

#Region "Update and Draw"


	''' <summary>
	''' Allows the screen the chance to position the menu entries. By default
	''' all menu entries are lined up in a vertical list, centered on the screen.
	''' </summary>
	Protected Overridable Sub UpdateMenuEntryLocations()
		' Make the menu slide into place during transitions, using a
		' power curve to make things look more interesting (this makes
		' the movement slow down as it nears the end).
		Dim transitionOffset! = CSng(Math.Pow(TransitionPosition, 2))

		' start at Y = 175 each X value is generated per entry
		Dim position As Vector2 = New Vector2(0.0F, 175.0F)

		' update each menu entry's location in turn
		For i% = 0 To menuEntries.Count - 1
			Dim _menuEntry As MenuEntry = menuEntries(i)

			' each entry is to be centered horizontally
			position.X = CSng(Me.ScreenManager.GraphicsDevice.Viewport.Width / 4 - _menuEntry.GetWidth(Me) / 4)

			If (ScreenState = ScreenState.TransitionOn) Then _
			 position.X -= transitionOffset * 256 _
			Else _
			 position.X += transitionOffset * 512

			' set the entry's position
			_menuEntry.Position = position

			' move down for the next entry the size of this entry plus our padding
			position.Y += _menuEntry.GetHeight(Me) + (menuEntryPadding * 2)
		Next
	End Sub

	''' <summary>
	''' Updates the menu.
	''' </summary>
	Public Overrides Sub Update(gameTime As GameTime, otherScreenHasFocus As Boolean, coveredByOtherScreen As Boolean)
		MyBase.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen)

		' Update each nested MenuEntry object.
		For i% = 0 To menuEntries.Count - 1

			Dim isSelected As Boolean = IsActive And (i = _selectedEntry)

			menuEntries(i).Update(Me, isSelected, gameTime)
		Next
	End Sub


	''' <summary>
	''' Draws the menu.
	''' </summary>
	Public Overrides Sub Draw(gameTime As GameTime)
		' make sure our entries are in the right place before we draw them
		UpdateMenuEntryLocations()

		Dim graphics As GraphicsDevice = ScreenManager.GraphicsDevice
		Dim _spriteBatch As SpriteBatch = ScreenManager.SpriteBatch
		Dim font As SpriteFont = ScreenManager.Font

		_spriteBatch.Begin()

		' Draw each menu entry in turn.
		For i% = 0 To menuEntries.Count - 1
			Dim _menuEntry As MenuEntry = menuEntries(i)

			Dim isSelected As Boolean = IsActive And (i = _selectedEntry)

			_menuEntry.Draw(Me, isSelected, gameTime)
		Next

		' Make the menu slide into place during transitions, using a
		' power curve to make things look more interesting (this makes
		' the movement slow down as it nears the end).
		Dim transitionOffset! = CSng(Math.Pow(TransitionPosition, 2))

		' Draw the menu title centered on the screen
		Dim titlePosition As Vector2 = New Vector2(CSng(graphics.Viewport.Width / 2), 80)
		Dim titleOrigin As Vector2 = font.MeasureString(_menuTitle) / 2
		Dim titleColor As Color = New Color(192, 192, 192) * TransitionAlpha
		Dim titleScale! = 1.25F

		titlePosition.Y -= transitionOffset * 100

		_spriteBatch.DrawString(font, _menuTitle, titlePosition, titleColor, 0, _
	titleOrigin, titleScale, SpriteEffects.None, 0)

		_spriteBatch.End()
	End Sub


#End Region
End Class